//ADDED FOR COMPATIBILITY WITH WIRING
extern "C" {
  #include <stdlib.h>
}

#include <WProgram.h>
#include "megaBrite.h"

#define clockpin   13 // CI
#define enablepin  10 // EI
#define latchpin   9  // LI
#define datapin    11 // DI

#define RGB_RES 1023  // Hard maximum luminance value

#ifndef MAX_CHANNELS
 #define MAX_CHANNELS 16
#endif


///////////////
//           //
//  CHANNEL  //
//           //
///////////////

channel::channel(){
  _modeFlag = 1;
  _smoothTimeoutFlag = 0;
  _smoothTime = 0;
  _startTime = millis();
  for(int i=0; i<3; i++){
    _startRGB[i] = 0;
    _targetRGB[i] = 0;
    _currentRGB[i] = 0;
  }
}

channel::channel(int pID){
	setChannelID(pID);
	channel();
}

int channel::getSmoothTime(){
	return _smoothTime;
}

int* channel::getRGB(){
	return _currentRGB;
}

int channel::getRed(){
	return _currentRGB[0];
}

int channel::getGreen(){
	return _currentRGB[1];
}

int channel::getBlue(){
	return _currentRGB[2];
}

int* channel::getTargetRBG(){
	return _targetRGB;
}

int channel::getTargetRed(){
	return _targetRGB[0];
}

int channel::getTargetGreen(){
	return _targetRGB[1];
}

int channel::getTargetBlue(){
	return _targetRGB[2];
}

int channel::getSmoothTimeoutFlag(){
	return _smoothTimeoutFlag;
}

int channel::getMode(){
        return _modeFlag;
}

void channel::setMode(int pMode){
	if(pMode != _modeFlag) _modeFlag = constrain(pMode,0,1);
	reset();
}

void channel::setRGB(int pRGB[3]){
	setRGB(pRGB[0], pRGB[1], pRGB[2]);
}

void channel::setRGB(int pRed, int pGreen, int pBlue){	//FIXED RESOLTION (dynamic better?)
	_targetRGB[0] = constrain(pRed,0,RGB_RES);
	_targetRGB[1] = constrain(pGreen,0,RGB_RES);
	_targetRGB[2] = constrain(pBlue,0,RGB_RES);
	reset();
}

void channel::setRed(int pRed){
	setRGB(pRed, getGreen(), getBlue());
}

void channel::setGreen(int pGreen){
	setRGB(getRed(), pGreen, getBlue());
}
		   
void channel::setBlue(int pBlue){
	setRGB(getRed(), getGreen(), pBlue);
}

void channel::setChannelID(int pID){
	_channelID = pID;
}

int* channel::step(){
        unsigned long elapsedTime;
	if(!_smoothTimeoutFlag){
            if(_smoothTime){
		elapsedTime = millis() - _startTime;
		if (elapsedTime > _smoothTime) {
			elapsedTime = _smoothTime;
			_smoothTimeoutFlag = 1;
		}
                _modeFlag = 1;  // !!!!!!!!
		for (int i = 0; i<3; i++) {
			_currentRGB[i] =  _startRGB[i] + (int)( ((unsigned long)((_targetRGB[i] * _modeFlag) - _startRGB[i]) * elapsedTime) / (unsigned long)(_smoothTime) );  //DIVISION BY 0 WHEN _smoothTime = 0 !!
		}
            }
            else{
                for (int i = 0; i<3; i++) {
                  _modeFlag = 1;  // !!!!!!!!!!
                  _currentRGB[i] = _targetRGB[i] * _modeFlag;
                }
                _smoothTimeoutFlag = 1;
            }
	}
	return getRGB();
}

void channel::setSmoothTime(int pSmoothTime){
	_smoothTime = max(pSmoothTime, 0);
}

void channel::reset(){
	for (int i = 0; i<3; i++) {
		_startRGB[i] = _currentRGB[i];
	}
	_smoothTimeoutFlag = 0;
	_startTime = millis();
}


/////////////////
//             //
//  MEGABRITE  //
//             //
/////////////////

megaBrite::megaBrite(){
        megaBrite(1);
}

megaBrite::megaBrite(int pChannels){
  	pinMode(datapin, OUTPUT);			//physical init
	pinMode(latchpin, OUTPUT);
	pinMode(enablepin, OUTPUT);
	pinMode(clockpin, OUTPUT);
	SPCR = (1<<SPE)|(1<<MSTR)|(0<<SPR1)|(0<<SPR0);
	digitalWrite(latchpin, LOW);
	digitalWrite(enablepin, LOW);
	
	for(int i=0;i<MAX_CHANNELS;i++){	//construct channel objects
          channels[i] = channel(i);
	}
	setChannels(pChannels);
}
		   
int megaBrite::getChannels(){
	return _nChannels;
}

void megaBrite::setChannels(int pChannels){
	_nChannels = pChannels;
}

void megaBrite::setSmoothTime(int pSmoothTime){
	for (int i=0; i<MAX_CHANNELS; i++) {
		channels[i].setSmoothTime(pSmoothTime);
	}
}
		
void megaBrite::setRGB(int pRGB[3]){
	setRGB(pRGB[0], pRGB[1], pRGB[2]);
}

void megaBrite::setRGB(int pRed, int pGreen, int pBlue){
	for (int i=0; i<_nChannels; i++) {
		channels[i].setRGB(pRed, pGreen, pBlue);
	}
}

void megaBrite::setRed(int pRed){
  	for (int i=0; i<_nChannels; i++) {
	    channels[i].setRed(pRed);
	}
}

void megaBrite::setGreen(int pGreen){
	for (int i=0; i<_nChannels; i++) {
	    channels[i].setGreen(pGreen);
	};
}
		
void megaBrite::setBlue(int pBlue){
  	for (int i=0; i<_nChannels; i++) {
	    channels[i].setBlue(pBlue);
	}
}

void megaBrite::setMode(int pMode){
   for (int i=0; i<_nChannels; i++) {
	   channels[i].setMode(pMode);
   }
}
		   
void megaBrite::update() {
   _SB_CommandMode = B00; // Write to PWM control registers
   for (int i=_nChannels-1; i>=0; i--) {
	   channels[i].step();
	   _SB_RedCommand   = channels[i].getRed();
	   _SB_GreenCommand = channels[i].getGreen();
	   _SB_BlueCommand  = channels[i].getBlue();
	   SB_SendPacket();
   }
   latchData();
}

void megaBrite::SB_SendPacket() {
	if (_SB_CommandMode == B01) {
		_SB_RedCommand = 120;
		_SB_GreenCommand = 100;
		_SB_BlueCommand = 100;
	}	
	
	SPDR = _SB_CommandMode << 6 | _SB_BlueCommand>>4;
	while(!(SPSR & (1<<SPIF)));
	SPDR = _SB_BlueCommand<<4 | _SB_RedCommand>>6;
	while(!(SPSR & (1<<SPIF)));
	SPDR = _SB_RedCommand << 2 | _SB_GreenCommand>>8;
	while(!(SPSR & (1<<SPIF)));
	SPDR = _SB_GreenCommand;
	while(!(SPSR & (1<<SPIF)));	
}

void megaBrite::latchData() {
	delayMicroseconds(15);
	digitalWrite(latchpin,HIGH); // latch data into registers
	delayMicroseconds(15);
	digitalWrite(latchpin,LOW);
	
	_SB_CommandMode = B01; // Write to current control registers
	for (int i=0; i<_nChannels; i++) SB_SendPacket();
	delayMicroseconds(15);
	digitalWrite(latchpin,HIGH); // latch data into registers
	delayMicroseconds(15);
	digitalWrite(latchpin,LOW);
}


